package com.easylive.service.impl;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.annotation.Resource;


import ch.qos.logback.core.joran.util.beans.BeanUtil;
import com.easylive.component.EsSearchComponent;
import com.easylive.component.RedisComponent;
import com.easylive.entity.config.AppConfig;
import com.easylive.entity.constants.Constants;
import com.easylive.entity.dto.SysSettingDto;
import com.easylive.entity.dto.UploadingFileDto;
import com.easylive.entity.enums.*;
import com.easylive.entity.po.VideoInfo;
import com.easylive.entity.po.VideoInfoFile;
import com.easylive.entity.po.VideoInfoFilePost;
import com.easylive.entity.query.*;
import com.easylive.exception.BusinessException;
import com.easylive.mappers.VideoInfoFileMapper;
import com.easylive.mappers.VideoInfoFilePostMapper;
import com.easylive.mappers.VideoInfoMapper;
import com.easylive.service.VideoInfoFilePostService;
import com.easylive.service.VideoInfoFileService;
import com.easylive.utils.CopyTools;
import com.easylive.utils.FFmpegUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.easylive.entity.po.VideoInfoPost;
import com.easylive.entity.vo.PaginationResultVO;
import com.easylive.mappers.VideoInfoPostMapper;
import com.easylive.service.VideoInfoPostService;
import com.easylive.utils.StringTools;
import org.springframework.transaction.annotation.Transactional;


/**
 * 视频信息表 业务接口实现
 */
@Slf4j
@Service("videoInfoPostService")
public class VideoInfoPostServiceImpl implements VideoInfoPostService {

	@Resource
	private VideoInfoPostMapper<VideoInfoPost, VideoInfoPostQuery> videoInfoPostMapper;
	@Resource
	private VideoInfoMapper<VideoInfo, VideoInfoQuery> videoInfoMapper;
	@Resource
	private VideoInfoFileMapper<VideoInfoFile, VideoInfoFileQuery> videoInfoFileMapper;
	@Autowired
    private RedisComponent redisComponent;
	@Autowired
	private EsSearchComponent esSearchComponent;
	@Resource
	private VideoInfoFilePostMapper<VideoInfoFilePost, VideoInfoFilePostQuery> videoInfoFilePostMapper;
	@Resource
	private AppConfig appConfig;
	@Resource
	private FFmpegUtils ffmpegUtils;

	/**
	 * 根据条件查询列表
	 */
	@Override
	public List<VideoInfoPost> findListByParam(VideoInfoPostQuery param) {
		return this.videoInfoPostMapper.selectList(param);
	}

	/**
	 * 根据条件查询列表
	 */
	@Override
	public Integer findCountByParam(VideoInfoPostQuery param) {
		return this.videoInfoPostMapper.selectCount(param);
	}

	/**
	 * 分页查询方法
	 */
	@Override
	public PaginationResultVO<VideoInfoPost> findListByPage(VideoInfoPostQuery param) {
		int count = this.findCountByParam(param);
		int pageSize = param.getPageSize() == null ? PageSize.SIZE15.getSize() : param.getPageSize();

		SimplePage page = new SimplePage(param.getPageNo(), count, pageSize);
		param.setSimplePage(page);
		List<VideoInfoPost> list = this.findListByParam(param);
		PaginationResultVO<VideoInfoPost> result = new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
		return result;
	}

	/**
	 * 新增
	 */
	@Override
	public Integer add(VideoInfoPost bean) {
		return this.videoInfoPostMapper.insert(bean);
	}

	/**
	 * 批量新增
	 */
	@Override
	public Integer addBatch(List<VideoInfoPost> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.videoInfoPostMapper.insertBatch(listBean);
	}

	/**
	 * 批量新增或者修改
	 */
	@Override
	public Integer addOrUpdateBatch(List<VideoInfoPost> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.videoInfoPostMapper.insertOrUpdateBatch(listBean);
	}

	/**
	 * 多条件更新
	 */
	@Override
	public Integer updateByParam(VideoInfoPost bean, VideoInfoPostQuery param) {
		StringTools.checkParam(param);
		return this.videoInfoPostMapper.updateByParam(bean, param);
	}

	/**
	 * 多条件删除
	 */
	@Override
	public Integer deleteByParam(VideoInfoPostQuery param) {
		StringTools.checkParam(param);
		return this.videoInfoPostMapper.deleteByParam(param);
	}

	/**
	 * 根据VideoId获取对象
	 */
	@Override
	public VideoInfoPost getVideoInfoPostByVideoId(String videoId) {
		return this.videoInfoPostMapper.selectByVideoId(videoId);
	}

	/**
	 * 根据VideoId修改
	 */
	@Override
	public Integer updateVideoInfoPostByVideoId(VideoInfoPost bean, String videoId) {
		return this.videoInfoPostMapper.updateByVideoId(bean, videoId);
	}

	/**
	 * 根据VideoId删除
	 */
	@Override
	public Integer deleteVideoInfoPostByVideoId(String videoId) {
		return this.videoInfoPostMapper.deleteByVideoId(videoId);
	}

	@Override
	@Transactional
	public void saveVideoInfo(VideoInfoPost videoInfoPost, List<VideoInfoFilePost> filePostList) {
		if(filePostList.size() > redisComponent.getSysSettingDto().getVideoPCount()){
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}//分p大小限制

		if(!StringTools.isEmpty(videoInfoPost.getVideoId())){//如果不是新增
			VideoInfoPost videoInfoPostDb = this.videoInfoPostMapper.selectByVideoId(videoInfoPost.getVideoId());
			if(videoInfoPostDb == null){
				throw new BusinessException(ResponseCodeEnum.CODE_600);
			}//查不到数据，不能修改
			if(ArrayUtils.contains(new Integer[]{VideoStatusEnum.STATUS0.getStatus(),
												VideoStatusEnum.STATUS2.getStatus()}, videoInfoPostDb.getStatus())){
				throw new BusinessException(ResponseCodeEnum.CODE_600);
			}//视频还在转码或审核，不能修改

		}


		Date curDate = new Date();
		String videoId = videoInfoPost.getVideoId();
		List<VideoInfoFilePost> deleteFileList = new ArrayList<>();
		List<VideoInfoFilePost> addFileList = filePostList;

		if(StringTools.isEmpty(videoId)){//如果是新增
			videoId = StringTools.getRandomString(Constants.Length_10);//新建id
			videoInfoPost.setVideoId(videoId);
			videoInfoPost.setCreateTime(curDate);
			videoInfoPost.setLastUpdateTime(curDate);
			videoInfoPost.setStatus(VideoStatusEnum.STATUS0.getStatus());
			this.videoInfoPostMapper.insert(videoInfoPost);
		}else{
			VideoInfoFilePostQuery fileQuery = new VideoInfoFilePostQuery();
			fileQuery.setVideoId(videoId);
			fileQuery.setUserId(videoInfoPost.getUserId());
			List<VideoInfoFilePost> dbInfoFileList = videoInfoFilePostMapper.selectList(fileQuery);
			//从数据库查询已经上传的文件表

			Map<String,VideoInfoFilePost> uploadFileMap = filePostList.stream().collect(Collectors.toMap(item -> item.getUploadId(),Function.identity(),
					(data1,data2) -> data2));
			//根据uploadid分类本次上传的文件列表


			Boolean updateFileName = false;
			for(VideoInfoFilePost fileInfo : dbInfoFileList){//循环遍历数据库视频表
				VideoInfoFilePost updateFile = uploadFileMap.get(fileInfo.getUploadId());
				if(updateFile == null) {
					deleteFileList.add(fileInfo);//从上传的文件查找数据库文件，找不到说明已删除。
				}else if(!updateFile.getFileName().equals(fileInfo.getFileName())){
					updateFileName = true;
				}

			}
			addFileList = filePostList.stream().filter(item -> item.getFileId() == null).collect(Collectors.toList());

			videoInfoPost.setLastUpdateTime(curDate);
			Boolean changeVideoInfo = this.changeVideoInfo(videoInfoPost);
			if(!addFileList.isEmpty()){
				videoInfoPost.setStatus(VideoStatusEnum.STATUS0.getStatus());
			}else if(changeVideoInfo || updateFileName){
				videoInfoPost.setStatus(VideoStatusEnum.STATUS2.getStatus());
			}

			this.videoInfoPostMapper.updateByVideoId(videoInfoPost, videoId);


		}
		if(!deleteFileList.isEmpty()){
			List<String> delFileList = deleteFileList.stream().map(item -> item.getFileId()).collect(Collectors.toList());
			this.videoInfoFilePostMapper.deleteBatchByFileId(delFileList, videoInfoPost.getUserId());

			List<String> delFilePathList = deleteFileList.stream().map(item -> item.getFilePath()).collect(Collectors.toList());
			redisComponent.addFile2DelQueue(videoId,delFilePathList);//消息队列存放删除视频
		}
		Integer index = 1;
		for(VideoInfoFilePost videoInfoFile : filePostList){
			videoInfoFile.setFileIndex(index++);
			videoInfoFile.setVideoId(videoId);
			videoInfoFile.setUserId(videoInfoPost.getUserId());
			if(videoInfoFile.getFileId() == null){
				videoInfoFile.setFileId(StringTools.getRandomString(Constants.Length_20));
				videoInfoFile.setUpdateType(VideoFileUpdateTypeEnum.UPDATE.getStatus());
				videoInfoFile.setTransferResult(VideoFileTransferResultEnum.TRANSFER.getStatus());
			}

		}
		this.videoInfoFilePostMapper.insertOrUpdateBatch(filePostList);

		if(addFileList != null && !addFileList.isEmpty()){
			for(VideoInfoFilePost fileInfo : addFileList){
				fileInfo.setUserId(videoInfoPost.getUserId());
				fileInfo.setVideoId(videoId);

			}
			redisComponent.addFile2TransferQueue(addFileList);
		}



	}

	private Boolean changeVideoInfo(VideoInfoPost videoInfoPost){

		VideoInfoPost dbInfo = this.videoInfoPostMapper.selectByVideoId(videoInfoPost.getVideoId());
		if(!videoInfoPost.getVideoCover().equals(dbInfo.getVideoCover())
				||!videoInfoPost.getVideoName().equals(dbInfo.getVideoName())
				||!videoInfoPost.getTags().equals(dbInfo.getTags())
				||!videoInfoPost.getIntroduction().equals(dbInfo.getIntroduction() == null ? "" : dbInfo.getIntroduction())){
			return true;

		}else {
			return false;
		}
	}


	@Override
	public void transferVideoFile(VideoInfoFilePost videoInfoFilePost) {
		VideoInfoFilePost updateFilePost = new VideoInfoFilePost();
		try{
			UploadingFileDto uploadingFileDto = redisComponent.getUploadingFileDto(videoInfoFilePost.getUserId(),videoInfoFilePost.getUploadId());
			String tempFilePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_TEMP + uploadingFileDto.getFilePath();
			File tempFile = new File(tempFilePath);

			String targetFilePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_VIDEO + uploadingFileDto.getFilePath();
			File targetFile = new File(targetFilePath);

			FileUtils.copyDirectory(tempFile, targetFile);

			FileUtils.forceDelete(tempFile);

			redisComponent.delVideoFileInfo(videoInfoFilePost.getUserId(),videoInfoFilePost.getUploadId());

			//合并文件

			String completeVideo = targetFilePath + Constants.TEMP_VIDEO_NAME;
			this.union(targetFilePath,completeVideo,true);

			//获取播放时长
			Integer duration = ffmpegUtils.getVideoInfoDuration(completeVideo);
			updateFilePost.setDuration(duration);
			updateFilePost.setFileSize(new File(completeVideo).length());
			updateFilePost.setFilePath(appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_VIDEO + uploadingFileDto.getFilePath());
			updateFilePost.setTransferResult(VideoFileTransferResultEnum.SUCCESS.getStatus());

			this.convertVideo2Ts(completeVideo);
		}catch(Exception e){
			log.error("转码失败",e);
			updateFilePost.setTransferResult(VideoFileTransferResultEnum.FAIL.getStatus());
		}finally {
			videoInfoFilePostMapper.updateByUploadIdAndUserId(updateFilePost,videoInfoFilePost.getUploadId(),videoInfoFilePost.getUserId());

			VideoInfoFilePostQuery videoInfoFilePostQuery = new VideoInfoFilePostQuery();
			videoInfoFilePostQuery.setVideoId(videoInfoFilePost.getVideoId());
			videoInfoFilePostQuery.setTransferResult(VideoFileTransferResultEnum.FAIL.getStatus());
			Integer failCount = videoInfoFilePostMapper.selectCount(videoInfoFilePostQuery);
			if(failCount > 0){
				VideoInfoPost videoInfoPost = new VideoInfoPost();
				videoInfoPost.setStatus(VideoStatusEnum.STATUS1.getStatus());
				videoInfoPostMapper.updateByVideoId(videoInfoPost, videoInfoFilePost.getVideoId());
				return;
			}
			videoInfoFilePostQuery.setTransferResult(VideoFileTransferResultEnum.TRANSFER.getStatus());
			Integer transferCount = videoInfoFilePostMapper.selectCount(videoInfoFilePostQuery);
			System.out.println(transferCount);
			if(transferCount == 0){
				Integer duration = videoInfoFilePostMapper.sumDuration(videoInfoFilePost.getVideoId());
				VideoInfoPost videoInfoPost = new VideoInfoPost();
				videoInfoPost.setStatus(VideoStatusEnum.STATUS2.getStatus());
				videoInfoPost.setDuration(duration);
				videoInfoPostMapper.updateByVideoId(videoInfoPost, videoInfoFilePost.getVideoId());

			}


		}


	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void auditVideo(String videoId, Integer status, String reason) {
		VideoStatusEnum videoStatusEnum = VideoStatusEnum.getByStatus(status);
		if(videoStatusEnum == null){
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}

		VideoInfoPost videoInfoPost = new VideoInfoPost();
		videoInfoPost.setStatus(status);

		VideoInfoPostQuery videoInfoPostQuery = new VideoInfoPostQuery();
		videoInfoPostQuery.setVideoId(videoId);
		videoInfoPostQuery.setStatus(VideoStatusEnum.STATUS2.getStatus());//乐观锁
		Integer auditCount = videoInfoPostMapper.updateByParam(videoInfoPost, videoInfoPostQuery);
		if(auditCount == 0){
			throw new BusinessException("审核失败");
		}

		VideoInfoFilePost videoInfoFilePost = new VideoInfoFilePost();
		videoInfoFilePost.setUpdateType(VideoFileUpdateTypeEnum.NO_UPDATE.getStatus());

		VideoInfoFilePostQuery videoInfoFilePostQuery = new VideoInfoFilePostQuery();

		videoInfoFilePostQuery.setVideoId(videoId);
		videoInfoFilePostMapper.updateByParam(videoInfoFilePost, videoInfoFilePostQuery);

		if(VideoStatusEnum.STATUS4 == videoStatusEnum){
			return;
		}

		VideoInfoPost infoPost = videoInfoPostMapper.selectByVideoId(videoId);

		VideoInfo dbVideoInfo = videoInfoMapper.selectByVideoId(videoId);
		if(dbVideoInfo == null){
			SysSettingDto sysSettingDto = redisComponent.getSysSettingDto();
			//TODO 给用户加硬币
		}

		VideoInfo videoInfo = new VideoInfo();
		BeanUtils.copyProperties(infoPost, videoInfo);
		videoInfoMapper.insertOrUpdate(videoInfo);

		VideoInfoFileQuery videoInfoFileQuery = new VideoInfoFileQuery();
		videoInfoFileQuery.setVideoId(videoId);
		videoInfoFileMapper.deleteByParam(videoInfoFileQuery);

		VideoInfoFilePostQuery videoInfoFilePostQuery2 = new VideoInfoFilePostQuery();
		videoInfoFilePostQuery2.setVideoId(videoId);
		List<VideoInfoFilePost> filePostList = videoInfoFilePostMapper.selectList(videoInfoFilePostQuery2);

		List<VideoInfoFile> fileList = CopyTools.copyList(filePostList, VideoInfoFile.class);

		videoInfoFileMapper.insertBatch(fileList);

		//删文件

		List<String> filePathList = redisComponent.getDelFileList(videoId);
		if(filePathList != null && filePathList.size() > 0){
			for(String filePath : filePathList){
				File file = new File(filePath);
				if(file.exists()){
					try {
						FileUtils.deleteDirectory(file);
					}catch(Exception e){
						log.error("删除文件失败",e);
					}
				}
			}
		}

		redisComponent.cleanFileDelList(videoId);

		//TODO保存到es
		esSearchComponent.saveDoc(videoInfo);

		//TODO消息队列



	}

	private void convertVideo2Ts(String completeVideo){
		File videoFile = new File(completeVideo);
		File tsFolder = videoFile.getParentFile();
		String codec = ffmpegUtils.getVideoCodec(completeVideo);
		if(codec.equals(Constants.VIDEO_CODE_HEVC)){
			String tempFileName = completeVideo+ Constants.VIDEO_CODE_TEMP_FILE_SUFFIX;
			new File(completeVideo).renameTo(new File(tempFileName));
			ffmpegUtils.convertHevc2Mp4(tempFileName,completeVideo);
			new File(tempFileName).delete();

		}

		ffmpegUtils.convertVideo2Ts(tsFolder,completeVideo);

		videoFile.delete();

	}

	private void union(String dirPath,String toFilePath,Boolean delSource){
		File dir = new File(dirPath);
		if(!dir.exists()){
			throw new BusinessException("目录不存在");
		}
		File fileList[] = dir.listFiles();
		File targetFile = new File(toFilePath);
		try(RandomAccessFile writeFile = new RandomAccessFile(targetFile, "rw")){
			byte[] b = new byte[1024 * 100];
			for(int i = 0; i < fileList.length; i++){
				int len = -1;
				File chunkFile = new File(dirPath + File.separator + i);
				RandomAccessFile readFile = null;
				try{
					readFile = new RandomAccessFile(chunkFile, "r");
					while((len = readFile.read(b)) != -1){
						writeFile.write(b, 0, len);
					}
				}catch(Exception e){
					log.error("合并文件失败",e);
					throw new BusinessException("合并文件失败");
				}finally{
						readFile.close();
				}

			}
		}catch (Exception e){
			throw new BusinessException("合并文件" + dirPath + "出错了");
		}finally {
			if(delSource){
				for(int i = 0; i < fileList.length; i++){
					fileList[i].delete();
				}
			}
		}




	}
}